/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * Data Block Class
 *      Provides a class to contain a block of sequential data.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;

namespace Adaptive.Foundation.IO
{
    /// <summary>
    /// Provides a class to contain a block of sequential data.
    /// </summary>
    public class DataBlock : DisposableObjectBase
    {
        /*--------------------------------------------------------------------------------
         * Private Constants Declarations
         *------------------------------------------------------------------------------*/
        #region Private Constants Declarations
        /// <summary>
        /// Default size of the data block.
        /// </summary>
        private const int DEF_BLOCK_SIZE = 65536;
        /// <summary>
        /// Error message.
        /// </summary>
        private const string BAD_SIZE_MSG = "Data block sizes must be in 4K sizes (multiples of 4096).";
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// Data block sequence.
        /// </summary>
        private int _sequence;
        /// <summary>
        /// Size of the data block.
        /// </summary>
        private int _blockSize = DEF_BLOCK_SIZE;
        /// <summary>
        /// Size of the actual data.
        /// </summary>
        private int _dataSize;
        /// <summary>
        /// Current data contents.
        /// </summary>
        private byte[] _data;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public DataBlock()
            : base()
        {
            _data = new byte[_blockSize];
            _data.Initialize();
        }
        /// <summary>
        /// Initializes a new instance of the class to the specified size.
        /// </summary>
        /// <param name="blockSize">
        /// The size of the data contained in the data block.
        /// </param>
        public DataBlock(int blockSize)
            : base()
        {
            if (blockSize % 4096 != 0)
                throw new ArgumentOutOfRangeException(BAD_SIZE_MSG);

            _blockSize = blockSize;
            _data = new byte[_blockSize];
            _data.Initialize();
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if ((!Disposed) && (disposing))
            {
                Clear();
            }

            _data = null;
            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets the size of the data block, in bytes.
        /// </summary>
        /// <value>
        /// The size of the data block.
        /// </value>
        public int BlockSize
        {
            get { return _blockSize; }
        }
        /// <summary>
        /// Gets the data currently stored in the data block.
        /// </summary>
        public byte[] Data
        {
            get
            {
                if (_data.Length == _dataSize)
                    return _data;
                else
                {
                    byte[] returnValue = new byte[_dataSize];
                    Array.Copy(_data, 0, returnValue, 0, _dataSize);
                    return returnValue;
                }
            }
        }
        /// <summary>
        /// Gets or sets the ordinal / sequential index of the data block in a set.
        /// </summary>
        /// <value>
        /// A sequential numbering value used to order the data blocks.
        /// </value>
        public int Sequence
        {
            get { return _sequence; }
            set
            {
                _sequence = value;
            }
        }
        /// <summary>
        /// Gets the size of the actual data in the data block.
        /// </summary>
        /// <value>
        /// This will be the value of <see cref="BlockSize"/> except for the last
        /// data block in a set, which may be smaller than the others.
        /// </value>
        public int DataSize
        {
            get { return _dataSize; }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods / Event Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods / Event Methods
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Clears the contents of the object instance without disposing of the instance.
        /// </summary>
        public void Clear()
        {
            _data = new byte[_blockSize];
            _data.Initialize();
            _dataSize = 0;
            _sequence = 0;
        }
        /// <summary>
        /// Reads the data content from the supplied reader instance.
        /// </summary>
        /// <param name="reader">
        /// An open <see cref="BinaryReader"/> instance used to read data from
        /// an open stream.
        /// </param>
        /// <remarks>
        /// The data, data size, and sequence number values will be determined when
        /// the data is read.
        /// </remarks>
        /// <returns>
        /// <b>true</b> if the last of the data was read; otherwise, <b>false</b>.
        /// </returns>
        public bool FromStream(BinaryReader reader)
        {
            byte[] data = null;                 //Data content.
            bool returnValue = false;           //Return value.

            //Get the current sequence.
            _sequence = (int)((float)reader.BaseStream.Position / (float)_blockSize);

            //Read the data.
            data = reader.ReadBytes(_blockSize);
            _data.Initialize();
            Array.Copy(data, 0, _data, 0, data.Length);

            _dataSize = data.Length;
            data = null;
            returnValue = !(reader.BaseStream.Position < reader.BaseStream.Length);
            return returnValue;
        }
        /// <summary>
        /// Writes the contents of the block to the open stream.
        /// </summary>
        /// <param name="writer">
        /// An open <see cref="BinaryWriter"/> instance used to write data to
        /// an open stream.
        /// </param>
        public void ToStream(BinaryWriter writer)
        {
            //Go to the correct location.
            writer.Seek((_sequence * _blockSize), SeekOrigin.Begin);

            //Write the data.
            writer.Write(_data, 0, _dataSize);
        }
        #endregion
    }
}
